﻿namespace SPDeploy.Library.Logging
{
    using System.Collections.Generic;

    public class LineSplitter
    {
        #region Fields: private

        private string _currentLine;
        private string _input;
        private int _lastSeparatorIndex;
        private int _maxConsecutiveBlankLines = 1;
        private int _numberOfBlankLines;
        private List<string> _processedLines;
        private string _separators = @" .,-|?!\/@'"":;_&";

        #endregion

        public LineSplitter(int maxLineLength, string input)
        {
            InitializeInput(input);
            InitializeState(maxLineLength);
            SplitIntoLines(maxLineLength);
        }

        #region Properties: public

        public List<string> Lines
        {
            get { return _processedLines; }
        }

        public int MaxConsecutiveBlankLines
        {
            get { return _maxConsecutiveBlankLines; }
            set { _maxConsecutiveBlankLines = value; }
        }

        public string Separators
        {
            get { return _separators; }
            set { _separators = value; }
        }

        #endregion

        #region Properties: private

        private bool CurrentLineContainsSeparator
        {
            get { return _lastSeparatorIndex != -1; }
        }

        #endregion

        #region Methods: private

        private static int GetIndexOfFirstNonWhiteSpace(string input)
        {
            for (int i = 0; i < input.Length; ++i)
            {
                if ("\t ".IndexOf(input[i]) == -1)
                {
                    return i;
                }
            }
            return input.Length;
        }

        private void InitializeInput(string input)
        {
            _input = input.Replace("\r", "").Replace("\t", "    ").Trim('\n');
            RemoveCommonPadding(ref _input);
        }

        private void InitializeState(int maxLineLength)
        {
            _processedLines = new List<string>();
            _currentLine = "";
            _lastSeparatorIndex = -1;
        }

        private bool IsSeparator(char curChar)
        {
            return Separators.IndexOf(curChar) != -1;
        }

        private bool NextCharIsSeparator(int index)
        {
            return index + 1 < _input.Length && IsSeparator(_input[index + 1]);
        }

        private static void RemoveCommonPadding(ref string input)
        {
            var lines = new List<string>(input.Split('\n'));
            int commonPadding = -1;
            foreach (string line in lines)
            {
                int whiteSpace = GetIndexOfFirstNonWhiteSpace(line);
                if (commonPadding == -1 || whiteSpace < commonPadding)
                {
                    commonPadding = whiteSpace;
                }
            }
            if (commonPadding > 0)
            {
                for (int i = 0; i < lines.Count; ++i)
                {
                    lines[i] = lines[i].Remove(0, commonPadding);
                }
                input = string.Join("\n", lines.ToArray());
            }
        }

        private void SaveCurrentLine()
        {
            if (string.IsNullOrEmpty(_currentLine.Trim()))
            {
                ++_numberOfBlankLines;
            }
            else
            {
                _numberOfBlankLines = 0;
            }

            if (_numberOfBlankLines <= MaxConsecutiveBlankLines)
            {
                _processedLines.Add(_currentLine);
            }

            _currentLine = "";
            _lastSeparatorIndex = -1;
        }

        private void SplitIntoLines(int maxLineLength)
        {
            for (int index = 0; index < _input.Length; ++index)
            {
                char curChar = _input[index];
                if (curChar == '\n')
                {
                    SaveCurrentLine();
                    continue;
                }

                _currentLine += curChar;
                int indexIntoCurrentLine = _currentLine.Length - 1;

                if (IsSeparator(curChar))
                {
                    _lastSeparatorIndex = indexIntoCurrentLine;
                }

                if (_currentLine.Length == maxLineLength)
                {
                    if (NextCharIsSeparator(index) || !CurrentLineContainsSeparator)
                    {
                        SaveCurrentLine();
                        continue;
                    }
                    else
                    {
                        int charsToPutBack = _currentLine.Length - _lastSeparatorIndex - 1;
                        index -= charsToPutBack;
                        if (charsToPutBack > 0)
                        {
                            _currentLine = _currentLine.Remove(_currentLine.Length - charsToPutBack);
                        }
                        SaveCurrentLine();
                        continue;
                    }
                }
            }
            if (_currentLine.Length > 0)
            {
                SaveCurrentLine();
            }
        }

        #endregion
    }
}